package com.wjh.service.impl;

import com.wjh.common.LockConstant;
import com.wjh.enums.UpdateUserAmountEnum;
import com.wjh.user.enums.StateEnum;
import com.wjh.user.request.UpdateAmountRequest;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson2.JSON;
import com.wjh.common.BaseInfo;
import com.wjh.common.CommonMethods;
import com.wjh.common.Constants;
import com.wjh.mapper.IUserMapper;
import com.wjh.service.IUserService;
import com.wjh.user.entity.UserEntity;
import com.wjh.user.request.SaveUserRequest;
import com.wjh.user.request.WebLoginRequest;
import com.wjh.user.response.UserWebLoginResponse;
import com.wjh.util.JsonUtils;
import com.wjh.util.Md5Utils;
import com.wjh.util.RedisUtils;
import com.wjh.util.SnowFlakeIdWorker;

import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * 用户Service实现类
 * 
 * @author wenjianhai
 * @date 2023/6/21
 * @since JDK 17
 */
@Slf4j
@Service
public class UserServiceImpl implements IUserService {

	@Autowired
	private IUserMapper userMapper;

	@Autowired
	private RedisUtils redisUtils;

	/** 雪花算法 */
	@Autowired
	private SnowFlakeIdWorker snowFlakeIdWorker;

	@Autowired
	private RedissonClient redissonClient;

	@Override
	public void save(SaveUserRequest request, BaseInfo baseInfo) {

		UserEntity user = JsonUtils.parse(JsonUtils.toJSONBytes(request), UserEntity.class);

		if (StringUtils.isNotBlank(request.getPasswords())) {
			user.setPasswords(Md5Utils.encrypt(request.getPasswords()));
		}
		user.setUpdaterCode(baseInfo.getUserCode());
		user.setUpdaterName(baseInfo.getUserName());

		// 根据登录名查询用户信息
		UserEntity exist = userMapper.queryByLoginName(request.getLoginName());

		if (user.getId() == null || user.getId().intValue() == Constants.NUM_ZERO) {
			/* 新增 */
			if (exist != null) {
				log.error("新增用户信息,登录名已存在.登录名:{}", request.getLoginName());
				throw new RuntimeException("登录名已存在");
			}
			user.setUserGuid(CommonMethods.getMeUUID());
			user.setUserCode(this.generateCode());
			user.setAvailableAmount(0L);
			user.setCreatorCode(baseInfo.getUserCode());
			user.setCreatorName(baseInfo.getUserName());

			userMapper.save(user);
		} else {
			/* 修改 */
			if (exist != null && !request.getId().equals(exist.getId())) {
				log.error("修改用户信息,登录名已存在.登录名:{}", request.getLoginName());
				throw new RuntimeException("登录名已存在");
			}
			userMapper.update(user);
		}
		log.info("保存用户信息-结束.请求参数:{}", JSON.toJSONString(request));
	}

	@Override
	public UserEntity queryById(Long id) {
		return id == null ? null : userMapper.queryById(id);
	}

	/**
	 * PC端登录
	 *
	 * @param request 请求参数
	 * @return UserWebLoginResponse
	 * @date 2023-06-21
	 * @author wenjianhai
	 * @since JDK 17
	 */
	@Override
	public UserWebLoginResponse webLogin(WebLoginRequest request) {
		// 登录名
		String loginName = request.getLoginName();
		// 密码
		String password = request.getPasswords();

		// 根据登录名查询用户信息
		UserEntity user = userMapper.queryByLoginName(loginName);

		if (user == null) {
			log.error("PC端登录,登录名不存在");
			throw new RuntimeException("用户名不存在");
		}
		// 密码加密
		String md5Password = Md5Utils.encrypt(password);

		if (!md5Password.equals(user.getPasswords())) {
			log.error("PC端登录,密码错误.登录名:{}, 登录密码:{}, 加密后的登录密码:{}, 已有的密码:{}", loginName, password, md5Password,
					user.getPasswords());
			throw new RuntimeException("密码错误");
		}
		// 生成token
		String token = CommonMethods.getMeUUID();

		String rdsKey = Constants.LOGIN_TOKEN + token;

		BaseInfo baseInfo = JsonUtils.parse(JsonUtils.toJSONBytes(user), BaseInfo.class);
		baseInfo.setUserId(user.getId());

		// 缓存到Redis
		redisUtils.setObj(rdsKey, baseInfo, 30 * 60);

		UserWebLoginResponse response = UserWebLoginResponse.builder().token(token).user(user).build();

		log.info("PC端登录-结束.请求参数:{}, 返回结果:{}", JSON.toJSONString(request), JSON.toJSONString(response));

		return response;
	}

	/**
	 * 更新用户可用金额
	 *
	 * @param request 请求参数
	 * @date 2023-06-29
	 * @author wenjianhai
	 * @since JDK 17
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateAmount(UpdateAmountRequest request) {
		// 分布式锁
		RLock lock = null;

		try {
			String rdsKey = String.format(LockConstant.UPDATE_USER_AMOUNT_LOCK_NAME, request.getUserCode());
			// 分布式锁
			lock = redissonClient.getLock(rdsKey);

			if (!lock.tryLock(LockConstant.UPDATE_USER_AMOUNT_LOCK_WAIT_TIME,
					LockConstant.UPDATE_USER_AMOUNT_LOCK_LEASE_TIME, TimeUnit.SECONDS)) {
				log.info(request.getTip() + ",更新用户可用金额,获取锁失败.rdsKey:{}", rdsKey);
				if (lock.isLocked()) {
					lock.unlock();
				}
				return;
			}
			if (request.getAmount().longValue() <= Constants.NUM_ZERO) {
				log.info(request.getTip() + ",更新用户可用金额,请求金额小于或等于0.用户编码:{}", request.getUserCode());
				return;
			}
			// 查询用户信息
			UserEntity user = userMapper.queryByUserCodeForUpdate(request.getUserCode());

			if (user == null) {
				log.error(request.getTip() + ",更新用户可用金额,用户已不存在.用户编码:{}", request.getUserCode());
				throw new RuntimeException("用户已不存在");
			}
			if (!StateEnum.NORMAL.getCode().equals(user.getState())) {
				String stateName = StateEnum.getNameByValue(user.getState());
				log.error(request.getTip() + ",更新用户可用金额,用户已被{}.用户编码:{}", stateName, request.getUserCode());
				throw new RuntimeException("用户已被" + stateName);
			}
			if (UpdateUserAmountEnum.DEDUCT.getCode().equals(request.getFlag())
					&& (user.getAvailableAmount() == null || user.getAvailableAmount().longValue() <= 0
							|| request.getAmount().longValue() > user.getAvailableAmount().longValue())) {
				/* 扣除金额 */
				log.error(request.getTip() + ",更新用户可用金额,用户余额不足.用户编码:{}, 请求金额:{}, 用户余额:{}", request.getUserCode(),
						request.getAmount(), user.getAvailableAmount());
				throw new RuntimeException("余额不足");
			}
			if (UpdateUserAmountEnum.ADD.getCode().equals(request.getFlag())) {
				// 增加用户可用金额
				userMapper.addAmount(user.getUserGuid(), request.getAmount());
			}
			if (UpdateUserAmountEnum.DEDUCT.getCode().equals(request.getFlag())) {
				// 扣除用户可用金额
				userMapper.deductAmount(user.getUserGuid(), request.getAmount());
			}

		} catch (Exception e) {
			log.error(String.format("更新用户可用金额-失败.请求参数:%s", JSON.toJSONString(request)), e);
			throw new RuntimeException(e.getMessage());
		} finally {
			if (lock != null && lock.isLocked()) {
				lock.unlock();
			}
		}
	}

	private String generateCode() {
		String code = CommonMethods.getNumber(10);
		// 用户编码是否已存在
		Integer count = userMapper.isUserCodeExist(code);

		if (count != null && count.intValue() > Constants.NUM_ZERO) {
			// 用户编码已存在,继续生成
			code = generateCode();
		}
		return code;
	}

	private String getNo() {
		String flowNo = String.valueOf(this.snowFlakeIdWorker.nextId());
		int intMeLen = 22 - flowNo.length();
		if (intMeLen > 0) {
			flowNo = flowNo + CommonMethods.getNumber(intMeLen);
		}
		return flowNo;
	}
}
